/**
 * Axios请求工具封装
 * 功能概述：
 * - 统一处理HTTP请求与响应
 * - 自动添加认证Token
 * - 控制重复请求，避免并发冲突
 * - 全面的错误处理（网络错误、业务错误、状态码错误等）
 * - 错误提示节流机制，可通过配置关闭（interval ≤ 0时）
 * - 支持二进制数据处理（blob/arraybuffer）
 */
import axios from "axios";
import { ElMessage } from "element-plus";

// 创建axios实例，配置基础参数
const service = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API || "", // 基础API地址，从环境变量获取
  timeout: 10000, // 超时时间设置为10秒
  headers: { "Content-Type": "application/json;charset=utf-8" }, // 默认请求头
});

/**
 * 错误提示节流控制器
 * 用于限制短时间内重复显示相同错误提示
 * 当interval ≤ 0时，不进行节流控制（所有错误提示都会显示）
 */
const errorThrottle = {
  // 缓存错误记录：key为错误唯一标识，value为最后显示时间戳
  cache: new Map(),
  interval: 3000, // 节流间隔，单位毫秒（默认3秒，≤0时关闭节流）

  /**
   * 判断错误是否可以显示
   * @param {string} key - 错误唯一标识
   * @returns {boolean} true-可以显示，false-不显示
   */
  canShow(key) {
    // 核心判断：如果interval ≤ 0，则不进行节流，直接返回true
    if (this.interval <= 0) {
      return true;
    }

    const now = Date.now(); // 当前时间戳
    const lastTime = this.cache.get(key); // 获取该错误上次显示时间

    // 若没有记录或已超过间隔时间，则允许显示并更新记录
    if (!lastTime || now - lastTime > this.interval) {
      this.cache.set(key, now);
      return true;
    }
    return false;
  },

  /**
   * 清除指定错误的记录
   * @param {string} key - 错误标识
   */
  clear(key) {
    this.cache.delete(key);
  },

  /**
   * 清除所有错误记录
   */
  clearAll() {
    this.cache.clear();
  },

  /**
   * 设置节流间隔
   * @param {number} ms - 间隔时间（毫秒），≤0时关闭节流
   */
  setInterval(ms) {
    this.interval = ms;
  },
};

/**
 * 请求控制器映射表
 * 用于存储和管理请求的取消控制器，实现重复请求取消功能
 * key: 请求唯一标识，value: AbortController实例
 */
const abortMap = new Map();

/**
 * 401错误处理状态标记
 * 防止在并发401错误时，多次执行跳转登录页的逻辑
 */
let isRefreshing = false;

/**
 * 生成请求的唯一标识
 * 用于判断两个请求是否为相同请求（方法、地址、参数均相同）
 * @param {Object} config - axios请求配置对象
 * @returns {string} 拼接后的唯一标识字符串
 */
const getKey = (config) => {
  const { method, url, params, data, baseURL } = config;
  // 处理绝对路径和相对路径，生成完整URL
  // 绝对路径（以http开头）直接使用，相对路径则拼接baseURL
  const fullUrl = url.startsWith("http") ? url : `${baseURL || ""}${url}`;

  // 拼接请求方法、URL、参数和请求体数据，生成唯一标识
  return [
    (method || "GET").toUpperCase(), // 请求方法统一转为大写
    fullUrl, // 完整请求地址
    JSON.stringify(params || {}), // URL参数序列化
    JSON.stringify(data || {}), // 请求体数据序列化
  ].join("&"); // 用&连接各部分，形成唯一标识字符串
};

/**
 * 请求拦截器
 * 在请求发送前进行预处理
 */
service.interceptors.request.use(
  (config) => {
    // 生成当前请求的唯一标识
    const key = config.manualKey || getKey(config);

    // 处理重复请求：如果未设置跳过检查且存在相同请求正在处理
    if (!config.skipDuplicateCheck && abortMap.has(key)) {
      abortMap.get(key).abort(); // 取消之前的相同请求
      abortMap.delete(key); // 移除之前的控制器
    }

    // 为请求创建取消控制器（如果没有自定义signal）
    if (!config.signal) {
      const controller = new AbortController();
      config.signal = controller.signal; // 关联取消信号
      abortMap.set(key, controller); // 存储控制器，用于后续取消
    }

    // 自动添加Token（优先级：请求配置中的token > 本地存储的token）
    const token = config.token || localStorage.getItem("token");
    if (token && !config.headers.Authorization) {
      config.headers.Authorization = `Bearer ${token}`;
    }

    return config;
  },
  (err) => {
    // 请求配置错误处理（应用节流控制）
    if (errorThrottle.canShow("config-error")) {
      ElMessage.error("请求配置错误");
    }
    return Promise.reject(err);
  }
);

/**
 * 响应拦截器
 * 对服务器响应进行统一处理
 */
service.interceptors.response.use(
  (res) => {
    const { config } = res;
    // 获取请求标识，用于清理资源
    const key = config.manualKey || getKey(config);
    // 移除已完成请求的控制器（不再需要取消）
    abortMap.delete(key);

    // 处理二进制数据响应（blob或arraybuffer）
    if (["blob", "arraybuffer"].includes(config.responseType)) {
      // 二进制数据为空判断
      if (
        (config.responseType === "blob" && res.data.size === 0) ||
        (config.responseType === "arraybuffer" && res.data.byteLength === 0)
      ) {
        // 显示错误提示（受节流控制）
        if (!config.skipErrorTip && errorThrottle.canShow("binary-empty")) {
          ElMessage.error("获取的二进制数据为空");
        }
        return Promise.reject(new Error("二进制数据为空"));
      }
      return res.data;
    }

    // 处理常规JSON响应 - 空数据判断
    const data = res.data;
    if (
      data === null ||
      data === undefined ||
      (typeof data === "object" && Object.keys(data).length === 0)
    ) {
      if (!config.skipErrorTip && errorThrottle.canShow("data-empty")) {
        ElMessage.error("请求成功但返回数据为空");
      }
      return Promise.reject(new Error("返回数据为空"));
    }

    // 正常业务逻辑判断（基于后端返回的code或success字段）
    if (data.code === 200 || data.success) {
      // 显示成功提示（默认显示，可通过配置关闭）
      if (config.showSuccessTip !== false) {
        // 成功提示文本优先级：用户自定义 > 后端返回msg > 默认文本
        ElMessage.success(config.successText || data.msg || "操作成功");
      }
      // 优先返回data字段，没有则返回整个响应数据
      return data.data ?? data;
    } else {
      // 业务错误处理（用错误消息作为节流标识）
      const errorKey = `biz-${data.msg || "unknown"}`;
      if (!config.skipErrorTip && errorThrottle.canShow(errorKey)) {
        ElMessage.error(data.msg || "操作失败");
      }
      return Promise.reject(new Error(data.msg || "请求异常"));
    }
  },
  (err) => {
    // 清理已完成请求的控制器
    if (err.config) {
      const key = err.config.manualKey || getKey(err.config);
      abortMap.delete(key);
    }

    // 处理取消请求的错误
    if (["AbortError", "CanceledError"].includes(err.name)) {
      return Promise.reject(new Error("请求已取消"));
    }

    // 错误信息处理
    let message; // 错误提示文本
    let errorKey; // 错误唯一标识（用于节流）

    // 跨域错误判断
    if (
      !err.response &&
      (err.message.includes("CORS") ||
        err.message.includes("Network Error") ||
        err.message.includes("Failed to fetch"))
    ) {
      message = "跨域请求失败，请联系管理员配置跨域权限";
      errorKey = "cors-error";
    }
    // 超时错误处理
    else if (err.code === "ECONNABORTED" || err.message.includes("timeout")) {
      message = "请求超时，请稍后重试";
      errorKey = "timeout-error";
    }
    // 其他HTTP状态码错误处理
    else {
      const status = err.response?.status;
      // 状态码与提示文本的映射表
      const msgMap = {
        400: "请求参数错误，请检查输入",
        401: "身份验证失败，请重新登录",
        403: "没有权限执行该操作",
        404: "请求的资源不存在",
        429: "请求过于频繁",
        500: "服务器内部错误",
      };

      // 400错误优先使用后端返回的具体信息
      if (status === 400 && err.response?.data?.msg) {
        message = err.response.data.msg;
        errorKey = `400-${message}`; // 用具体消息作为标识
      } else {
        message = msgMap[status] || `请求错误(${status || "未知"})`;
        errorKey = `status-${status || "unknown"}`; // 用状态码作为标识
      }
    }

    // 401错误自动跳转登录页处理（只执行一次）
    if (
      err.response?.status === 401 &&
      !err.config?.skipLoginRedirect &&
      !isRefreshing
    ) {
      isRefreshing = true; // 标记为正在处理
      setTimeout(() => {
        window.location.href = "/login"; // 跳转到登录页
        isRefreshing = false; // 处理完成后重置标记
      }, 1000);
    }

    // 显示错误提示（应用节流控制）
    if (!err.config?.skipErrorTip && errorThrottle.canShow(errorKey)) {
      ElMessage.error(message);
    }

    return Promise.reject(new Error(message));
  }
);

/**
 * 核心请求方法
 * 整合所有请求配置，作为所有API请求的入口点
 * @param {Object} config - 请求配置参数对象，可覆盖默认配置
 * @param {string} [config.method='get'] - HTTP请求方法
 * @param {string} config.url - 请求的URL地址（必填项）
 * @param {Object} [config.params] - URL查询参数（适用于GET请求）
 * @param {Object} [config.data] - 请求体数据（适用于POST/PUT等请求）
 * @param {string} [config.responseType='json'] - 期望的响应数据类型
 * @param {number} [config.timeout] - 超时时间（毫秒）
 * @param {boolean} [config.showSuccessTip=true] - 是否显示成功提示
 * @param {string} [config.successText] - 自定义成功提示文本
 * @param {boolean} [config.skipDuplicateCheck=false] - 是否跳过重复请求检查
 * @param {boolean} [config.skipErrorTip=false] - 是否跳过错误提示
 * @param {boolean} [config.skipLoginRedirect=false] - 是否跳过401跳转
 * @param {string} [config.manualKey] - 手动指定请求唯一标识
 * @param {string} [config.token] - 临时指定的Token
 * @param {Object} [config.headers] - 自定义请求头
 * @returns {Promise} 返回Promise对象
 */
const request = (config = {}) =>
  service({
    method: "get", // 默认请求方法为GET
    showSuccessTip: true, // 默认显示成功提示
    skipDuplicateCheck: false, // 默认不跳过重复请求检查
    skipErrorTip: false, // 默认不跳过错误提示
    skipLoginRedirect: false, // 默认不跳过401跳转
    ...config, // 用户配置覆盖默认配置
  });

// 快捷请求方法
export default {
  // 基础请求方法
  request,

  /**
   * GET请求快捷方法
   * @param {string} url - 请求地址
   * @param {Object} [config] - 请求配置参数
   * @returns {Promise}
   */
  get: (url, config) => request({ ...config, method: "get", url }),

  /**
   * POST请求快捷方法
   * @param {string} url - 请求地址
   * @param {Object} [data] - 请求体数据
   * @param {Object} [config] - 请求配置参数
   * @returns {Promise}
   */
  post: (url, data, config) =>
    request({ ...config, method: "post", url, data }),

  /**
   * PUT请求快捷方法
   * @param {string} url - 请求地址
   * @param {Object} [data] - 请求体数据
   * @param {Object} [config] - 请求配置参数
   * @returns {Promise}
   */
  put: (url, data, config) => request({ ...config, method: "put", url, data }),

  /**
   * DELETE请求快捷方法
   * @param {string} url - 请求地址
   * @param {Object} [config] - 请求配置参数
   * @returns {Promise}
   */
  delete: (url, config) => request({ ...config, method: "delete", url }),

  /**
   * 取消指定标识的请求
   * @param {string} key - 请求标识（manualKey）
   */
  cancel: (key) =>
    abortMap.has(key) && (abortMap.get(key).abort(), abortMap.delete(key)),

  /**
   * 取消所有未完成的请求
   */
  cancelAll: () => (
    abortMap.forEach((controller) => controller.abort()), abortMap.clear()
  ),

  /**
   * 错误节流控制方法
   */
  errorThrottle: {
    /**
     * 清除指定错误的节流记录
     * @param {string} key - 错误标识
     */
    clear: (key) => errorThrottle.clear(key),
    /**
     * 清除所有错误的节流记录
     */
    clearAll: () => errorThrottle.clearAll(),
    /**
     * 设置节流间隔
     * @param {number} ms - 间隔时间（毫秒），≤0时关闭节流
     */
    setInterval: (ms) => errorThrottle.setInterval(ms),
  },
};
